#ifndef H_MAPPED_FILE
#define H_MAPPPED_FILE

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#include "logger.hpp"
const long size_of_page_ = 20 * sysconf(_SC_PAGE_SIZE);
class MappedFile {
    public:
        enum Mode {
            READ = 0,
            WRITE = 1
        };
        MappedFile() {
            fd_ = 0;
        }
        ~MappedFile() {
        }

        bool Initialize(const char* file_name, Mode mode) {
            mode_ = mode;
            fd_ = open(file_name, (mode_ == READ)?O_RDONLY:O_RDWR|O_TRUNC|O_CREAT, 0644);

            if(fd_ == -1) {
                LogEr("can't open file");
                return false;
            }
            struct stat file_stat;
            if(fstat(fd_, &file_stat) < 0) {
                LogEr("can't fstat file");
                close(fd_);
                return false;
            }
            size_of_file_ = file_stat.st_size;
    
            //size_of_page_ = sysconf(_SC_PAGE_SIZE);
            if(size_of_page_ == -1) {
                LogEr("falled sysconf()");
                close(fd_);
                return false;
            }
            number_of_opened_page_ = -1;
            return true;
        }
        
        bool Finalize() {
            if(fd_ == -1) return true;

            if(number_of_opened_page_ != -1) {
                int rv = munmap(mapped_part_, size_of_page_);
                if(rv == -1) {
                    LogEr("Can't munmap");
                }
            }

            if(close(fd_) == -1) {
                LogEr("can't close file");
                return false;
            }

            return true;
        }

        char& operator[] (long i) {
            if(i >= size_of_file_) {
                int rv = ftruncate(fd_, size_of_page_ * ((i / size_of_page_) + 1));
                if(rv == -1) {
                    LogEr("failed ftrancate");
                }
                size_of_file_ = size_of_page_ * ((i / size_of_page_) + 1);
            }
            
            if(i < size_of_page_ * number_of_opened_page_ || i >= size_of_page_ *    
                (number_of_opened_page_ + 1)) { 
        
                if(number_of_opened_page_ != -1) {
                    munmap(mapped_part_, size_of_page_);
                }
            
                mapped_part_ = (char*)mmap(NULL, size_of_page_, (mode_ == READ)?PROT_READ:
                    PROT_WRITE|PROT_READ, MAP_SHARED, fd_, size_of_page_ * (i / size_of_page_));
                
 
                number_of_opened_page_ = i / size_of_page_;
            }
            return mapped_part_[i - (size_of_page_ * number_of_opened_page_)];
        }

        long GetSizeOfFile() {
            return size_of_file_;
        }
        
        
    private:
        //long size_of_page_;
        long size_of_file_;
        int fd_;
        char* mapped_part_;
        int number_of_opened_page_; //my brilliant solution
        Mode mode_;
};

#endif
